﻿using ApplicationModels.A02.Identities;
using Commons.Enums;
using Commons.Helpers;
using Commons.Responses;
using Emails;
using EntityModels.E02.Identities;
using IRepositories;
using JWT;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using System.Diagnostics;
using System.Security.Claims;
using System.Text.Json;

namespace ApplicationService
{
    public class AuthenticationService
    {
        private readonly ITokenService tokenService;
        private readonly IOptions<JWTOptions> optJWT;
        private readonly IEmailClient qqEmailClient;
        private readonly IAuthenticationRepository userRepository;
        private readonly IWeChatRepository weChatRepository;

        public AuthenticationService(
             ITokenService tokenService, IOptions<JWTOptions> optJWT, IEmailClient qqEmailClient, IAuthenticationRepository userRepository, IWeChatRepository weChatRepository)
        {
            this.tokenService = tokenService;
            this.optJWT = optJWT;
            this.qqEmailClient = qqEmailClient;
            this.userRepository = userRepository;
            this.weChatRepository = weChatRepository;
        }


        private async Task<SignInResult> CheckUserAndPwdAsync(Func<Task<AppUser>> func,string password)
        {
            var user = await func();
            if (user == null)
            {
                return SignInResult.Failed;
            }
            var result = await userRepository.CheckForSignInAsync(user, password, true);
            return result;
        }

        /// <summary>
        /// 邮箱验证码登录
        /// </summary>
        /// <param name="email"></param>
        /// <returns></returns>
        public async Task<SignResponse> LoginByEmailAndCodeAsync(string email)
        {
            var user = await userRepository.FindByEmailAsync(email);
            if (!user.LockoutEnabled)
            {
                return new SignResponse("账号被封禁!", false);
            }
            string token = await BuildTokenAsync(user);
            return new SignResponse("登录成功!", token);
        }


        public async Task<SignResponse> RegisterAndLoginByWechatAsync(LoginByWeChat lbw)
        {
            var wechatOptions = await GetOpenId(lbw.Code);
            if(wechatOptions == null)
            {
                return new SignResponse("登陆失败!", false);
            }
            var wechatUser = await weChatRepository.FindByOpenIdAsync(wechatOptions.Openid);
            var user = new AppUser();
            var token = "";
            if (wechatUser == null)
            {
                user.NickName = lbw.NickName;
                user.Avatar = lbw.Avatar;
                //创建用户需要userName和password，为了不重复我就通过时间戳了
                TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0);
                var pwd = Convert.ToInt64(ts.TotalSeconds).ToString();
                user.UserName = "YouTu_" + pwd;
                await userRepository.CreateAsync(user, pwd);
                user = await userRepository.FindByNameAsync(user.UserName);
                await userRepository.AddToRoleAsync(user, "user");
                //用新建的用户id和openid添加一条数据
                var newUser = new WeChatUser(user.Id, wechatOptions.Openid);
                var res = await weChatRepository.CreateWechatUserAsync(newUser);
            }
            else
            {
                user = await userRepository.FindByIdAsync(wechatUser.AppUserId);
                if (!user.LockoutEnabled)
                {
                    return new SignResponse("账号被封禁!", false);
                }
            }
            
            token = await BuildTokenAsync(user);
            return new SignResponse("登陆成功!", token);
        }

        /// <summary>
        /// 微信登录
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        public async Task<SignResponse> LoginByWeChatOpenIdAsync(string code)
        {
            var wechatOptions = await GetOpenId(code);
            //没获取到openid就登录失败了
            if (wechatOptions.Openid != null)
            {
                var user = new AppUser();
                var token = "";
                //查找数据库里有没有这个openid
                var chetWeChat = await weChatRepository.FindByOpenIdAsync(wechatOptions.Openid);
                if (chetWeChat != null)
                {
                    //有的话通过userid查找这个用户
                    user = await userRepository.FindByIdAsync(chetWeChat.AppUserId);
                    if (!user.LockoutEnabled)
                    {
                        return new SignResponse("账号被封禁!", false);
                    }

                }
                else
                {
                    return new SignResponse("账号不存在!", false);
                }
                //获取token
                token = await BuildTokenAsync(user);
                return new SignResponse("登陆成功!",token);

            }
            return new SignResponse( "登录失败!",false);
        }

        public async Task<WechatOptions> GetOpenId(string code)
        {
            string authUrl = "https://api.weixin.qq.com/sns/jscode2session?grant_type=authorization_code";
            //这里通过环境变量获取Id和密钥，你也可以改成明文
            string appId = Environment.GetEnvironmentVariable("AppID");
            string appSecret = Environment.GetEnvironmentVariable("AppSecret");
            //最后的url拼接
            authUrl = authUrl + "&appid=" + appId + "&secret=" + appSecret + "&js_code=" + code;
            var httpClient = new HttpClient();
            //发起get请求
            using HttpResponseMessage response = await httpClient.GetAsync(authUrl);
            var jsonString = await response.Content.ReadAsStringAsync();
            //反序列化成对象
            WechatOptions? wechatOptions = JsonSerializer.Deserialize<WechatOptions>(jsonString);
            return wechatOptions;
        }

        //<(SignInResult Result, string? Token)>  元组的语法
        /// <summary>
        /// 使用手机号码登录
        /// </summary>
        /// <param name="phoneNum"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public async Task<SignResponse> LoginByPhoneAndPwdAsync(string phoneNum, string password)
        {
            var checkResult = await CheckUserAndPwdAsync(async ()=> await userRepository.FindByPhoneNumberAsync(phoneNum), password);
            if (checkResult.Succeeded)
            {
                var user = await userRepository.FindByPhoneNumberAsync(phoneNum);
                if (!user.LockoutEnabled)
                {
                    return new SignResponse("账号被封禁!", false);
                }
                string token = await BuildTokenAsync(user);
                return new SignResponse("登录成功!", token);
            }
            else
            {
                return new SignResponse("登录失败!", false);
            }
        }

        public async Task<SignResponse> LoginByAccountAsync(string userName,string password)
        {
            var checkResult = await CheckUserAndPwdAsync(async () => await userRepository.FindByNameAsync(userName), password);
            if (checkResult.Succeeded)
            {
                var user = await userRepository.FindByNameAsync(userName);
                if (!user.LockoutEnabled)
                {
                    return new SignResponse("账号被封禁!", false);
                }
                string token = await BuildTokenAsync(user);
                return new SignResponse("登录成功!", token);
            }
            else
            {
                return new SignResponse("登录失败!", false);
            }
        }

        public async Task<AppUser> FindUserByPhoneAsync(string phone)
        {
            return await userRepository.FindByPhoneNumberAsync(phone);
        }
        public async Task<AppUser> FindUserByEmailAsync(string email)
        {
            return await userRepository.FindByEmailAsync(email);
        }


        public async Task<SignResponse> LoginByEmailAsync(string email, string password)
        {
            var checkResult = await CheckUserAndPwdAsync(async()=>await userRepository.FindByEmailAsync(email), password);
            if (checkResult.Succeeded)
            {
                var user = await userRepository.FindByEmailAsync(email);
                if (!user.LockoutEnabled)
                {
                    return new SignResponse("账号被封禁!", false);
                }
                string token = await BuildTokenAsync(user);
                return new SignResponse("登录成功!",token);
            }
            else
            {
                return new SignResponse("登录失败!", false);
            }
        }

        /// <summary>
        /// 手机号码注册
        /// </summary>
        /// <param name="phone"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public async Task<MessageResponse> RegisterByPhoneAsync(string phone, string password)
        {

            var user = await userRepository.FindByPhoneNumberAsync(phone);
            if (user == null)
            {

                user = await BuilderUser();
                user.PhoneNumber = phone;
                user.PhoneNumberConfirmed = true;
                var crUser = await userRepository.CreateAsync(user, password);
                Debug.Assert(crUser.Succeeded);
                var r = await userRepository.AddToRoleAsync(user, "user");
                Debug.Assert(r.Succeeded);
                return new MessageResponse("注册成功!");
            }
            else
            {
                return new MessageResponse("注册失败!");
            }
        }
        /// <summary>
        /// 账号密码注册
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="password"></param>
        /// <param name="role"></param>
        /// <returns></returns>
        /// <exception cref="CustomException"></exception>
        public async Task<MessageResponse> RegisterByAccount(string userName,string password,string role)
        {
            var user = await userRepository.FindByNameAsync(userName);
            if(user != null)
            {
                return new MessageResponse("注册失败!", false);
            }
            try
            {
                user = new AppUser(userName);
                await userRepository.CreateAsync(user, password);
                await userRepository.AddToRoleAsync(user, role);
                return new MessageResponse("注册成功!");
            }
            catch (Exception ex)
            {
                
                throw new CustomException("");
            }
        }

        /// <summary>
        /// 邮箱注册
        /// </summary>
        /// <param name="email"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public async Task<MessageResponse> RegisterByEmailAsync(string email, string password)
        {
            var user = await userRepository.FindByEmailAsync(email);
            if (user == null)
            {
                user = await BuilderUser();
                user.Email = email;
                user.EmailConfirmed = true;
                var crUser = await userRepository.CreateAsync(user, password);
                Debug.Assert(crUser.Succeeded);
                var r = await userRepository.AddToRoleAsync(user, "user");
                Debug.Assert(r.Succeeded);
                return new MessageResponse("注册成功!");
            }
            else
            {
                return new MessageResponse("注册失败!",false);
            }
        }

        public async Task<IdentityResult> ChangePasswordByEmailCode(string email,string password)
        {
            var user = await userRepository.FindByEmailAsync(email);
            return  await userRepository.ChangePasswordAsync(user.Id, password);

        }

        private async Task<AppUser> BuilderUser()
        {
            var ran = new Random();
            var id = ran.Next(1000000, 10000000);
            var userName = "108" + id.ToString();
            var user = await userRepository.FindByNameAsync(userName);
            while (user != null)
            {
                id = ran.Next(1000000, 10000000);
                userName = "108" + id.ToString();
                user = await userRepository.FindByNameAsync(userName);
            }
            user = new AppUser(userName);
            return user;
        }



        private async Task<string> BuildTokenAsync(AppUser user)
        {
            var roles = await userRepository.GetRolesAsync(user);
            List<Claim> claims = new List<Claim>();
            claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
            foreach (string role in roles)
            {
                claims.Add(new Claim(ClaimTypes.Role, role));
            }
            return tokenService.BuildToken(claims, optJWT.Value);
        }
        /// <summary>
        /// 邮件发送邮件
        /// </summary>
        /// <param name="email"></param>
        /// <returns></returns>
        public async Task<MessageResponse> EmailSendAsync(string email,EmailCodeTypeEnum e)
        {
            try
            {
                var redis = new RedisHelper();
                var redisKey = "register_";
                if (e == EmailCodeTypeEnum.Password)
                {
                    var user = await userRepository.FindByEmailAsync(email);
                    if (user == null)
                    {
                        return new MessageResponse(200, "当前邮箱不存在！", false);
                    }
                    redisKey = "password_";
                }
                Random rd = new Random();
                var code = rd.Next(100000, 999999).ToString();
                await redis.StringSetAsync(redisKey+email, code, TimeSpan.FromMinutes(15));
                var res = await qqEmailClient.SendEmailAsync(email, "请查收您的验证码", "您的验证码是：" + code + "\n请在15分钟内输入");
                if (res)
                {
                    return new MessageResponse("邮件已发送!");
                }
                else
                {
                    return new MessageResponse("发送出错!", false);
                }
            }
            catch (Exception ex)
            {
                if(ex.Message.Contains("It was not possible to connect to the redis server"))
                {
                    throw new CustomException("缓存系统崩啦!");
                }
                throw new CustomException("邮箱过期!");
            }
        }
    }
}
